/**
 * 防抖函数（常用于input框搜索情况）
 * @param {*} func 
 * @param {*} delay 
 * @param {*} immediate 
 * @returns 
 */
export function debounce(func, delay, immediate = true) {
  let timer = null
  return function(args) {
    let _this = this
    if (timer) {
      clearTimeout(timer)
    }
    if (immediate) {
      let now = !timer
      timer = setTimeout(() => {
        timer = null
      }, delay)
      now && func.call(_this, args)
    } else {
      timer = setTimeout(() => {
        timer = null
        func.call(_this, args)
      }, delay)
    }
  }
 }

/**
 * 节流函数（常用于onresize, onmouseover情况）
 * @param {*} func 
 * @param {*} delay 
 * @param {*} immediate 
 * @returns 
 */
export function throttle(func, delay, immediate = true) {
  let timer = null
  return function (args) {
    let _this = this
    if (!timer) {
      if (immediate) {
        func.call(_this, args)
        timer = setTimeout(() => {
          timer = null
        }, delay)
      } else {
        timer = setTimeout(() => {
          func.call(_this, args)
          timer = null
        }, delay)
      }
    }
  }
}

/**
 * 文件大小单位转换
 * @param size
 * @returns
 */
export function getfilesize(size) {
  if (!size) {
    return '';
  }
  const num = 1024.00;
  if (size < num) {
    return size + 'B';
  } else if (size < Math.pow(num, 2)) {
    return (size / num).toFixed(2) + 'K'; // kb
  } else if (size < Math.pow(num, 3)) {
    return (size / Math.pow(num, 2)).toFixed(2) + 'M'; // M
  } else if (size < Math.pow(num, 4)) {
    return (size / Math.pow(num, 3)).toFixed(2) + 'G'; // G
  } else {
    return (size / Math.pow(num, 4)).toFixed(2) + 'T'; // T
  }
}

/**
 * 深层级对象根据路径字符串赋值
 * @param {*} obj 对象
 * @param {*} path 路径字符串(由小数点分割)
 * @param {*} value 赋值
 */
export function deepSetValue(obj, path, value) {
  if (!path) {
    return
  }
  const keys = path.split('.');
  if (keys.length === 1) {
    obj[keys[0]] = value
  } else {
    const [curPath, ...nextPath] = keys
    if (!obj[curPath]) {
      obj[curPath] = {}
    }
    deepSetValue(obj[curPath], nextPath.join('.'), value)
  }
}

// 举例: deepSetValue({}, 'a.b', 'hello world') => {a: {b: 'hello world'}}

/**
 * 深层级对象根据路径字符串取值
 * @param {*} obj 对象
 * @param {*} path 路径字符串(由小数点分割)
 */
export function deepGetValue(obj, path) {
  let result = JSON.parse(JSON.stringify(obj))
  if (!path) {
    return result
  }
  const keys = path.split('.');
  for (const key of keys) {
    // 分区值为0的情况
    if (!result || (!result[key] && result[key] !== 0)) {
      return
    }
    result = result[key];
  }
  return result
}

// 举例: deepGetValue({a: {b: 'hello world'}}, 'a.b') => 'hello world'

/**
 * 深拷贝
 * @param {*} source
 * @returns 
 */
export function deepCopy(source) {
  // 创建一个 WeakMap 对象，记录已拷贝过的对象
  const weakmap = new WeakMap()
  // 获取数据类型，返回值如："Object"、"Array"、"Symbol" 等
  const getClass = x => {
    const type = Object.prototype.toString.call(x)
    return /^\[object (.*)\]$/.exec(type)[1]
  }
  // 判断是否为数组
  const isArray = arr => getClass(arr) === 'Array'
  // 判断是否为引用类型
  const isObject = obj => obj !== null && (typeof obj === 'object' || typeof obj === 'function')
  // 判断是否为“特殊”对象（需要特殊处理）
  const isSepcialObject = obj => {
    const type = getClass(obj)
    return ['Boolean', 'Number', 'String', 'Symbol', 'BigInt', 'Date', 'Map', 'Set', 'RegExp'].includes(type)
  }
  // 处理特殊对象
  const handleSepcialObject = obj => {
    const type = getClass(obj)
    const Ctor = obj.constructor // 对象的构造函数
    const primitiveValue = obj.valueOf() // 获取对象的原始值
    switch (type) {
      case 'Boolean':
      case 'Number':
      case 'String':
      case 'Symbol':
      case 'BigInt':
        // 处理包装对象 Wrapper Object
        return Object(primitiveValue)
      case 'Date':
        return new Ctor(primitiveValue) // new Date(+obj)
      case 'RegExp': {
        const { source, flags, lastIndex } = obj
        const re = new RegExp(source, flags)
        re.lastIndex = lastIndex
        return re
      }
      case 'Map': {
        const map = new Ctor()
        obj.forEach((item, key) => {
          // 注意，即使 Map 对象的 key 为引用类型，这里也不能 copy(key)，否则会失去引用，导致该属性无法访问得到。
          map.set(key, copy(item))
        })
        return map
      }
      case 'Set': {
        const set = new Ctor()
        obj.forEach(item => {
          set.add(copy(item))
        })
        return set
      }
      default:
        return undefined
    }
  }
  // 创建输出对象（原型拷贝关键就在这一步）
  const initCloneObject = obj => {
    if (obj.constructor === undefined) {
      return Object.create(null)
    }
    if (typeof obj.constructor === 'function' && (obj !== obj.constructor || obj !== Object.prototype)) {
      const proto = Object.getPrototypeOf(obj)
      return Object.create(proto)
    }
    return {}
  }
  // 拷贝方法（递归思路）
  const copy = input => {
    if (typeof input === 'function' || !isObject(input)) return input
    // 针对已拷贝过的对象，直接返回（解决循环引用的问题）
    if (weakmap.has(input)) {
      return weakmap.get(input)
    }
    // 处理包装对象
    if (isSepcialObject(input)) {
      return handleSepcialObject(input)
    }
    // 创建输出对象
    const output = isArray(input) ? [] : initCloneObject(input)
    // 记录每次拷贝的对象
    weakmap.set(input, output)
    // 仅遍历对象自身可枚举的属性（包括字符串属性和 Symbol 属性）
    Reflect.ownKeys(input).forEach(key => {
      output[key] = copy(input[key])
    })
    return output
  }
  return copy(source)
}
